package com.finalministry.db.dao;

import java.math.BigInteger;
import java.sql.SQLException;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.Query;

import org.hibernate.HibernateException;

import com.finalministry.db.factory.DatabaseFactory;

/**
 * Abstract Dao
 * <p>
 * Every Dao implementation should extend from this class.
 * </p>
 * 
 * @author lussopi
 * @param <T>
 */
public abstract class AbstractDAO<T> {

	protected EntityManager em;

	protected abstract String getTableName();

	protected T merge(T obj) {

		try {
			startOperation();
			obj = em.merge(obj);
			em.getTransaction().commit();
			return obj;
		} catch (HibernateException e) {
			handleException(e);
			return null;
		} finally {
			em.close();
		}
	}

	protected void delete(Object obj) {

		try {
			startOperation();
			em.remove(obj);
			em.getTransaction().commit();
		} catch (HibernateException e) {
			handleException(e);
		} finally {
			em.close();
		}
	}

	protected T find(Class<T> clazz, Long id) {

		T obj = null;
		try {
			startOperation();
			obj = em.find(clazz, id);
		} catch (HibernateException e) {
			handleException(e);
		} finally {
			em.close();
		}
		return obj;
	}

	protected void findAndDelete(Class<T> clazz, Long id) {

		T obj = null;
		try {
			startOperation();
			obj = em.find(clazz, id);
			em.remove(obj);
			em.getTransaction().commit();
		} catch (HibernateException e) {
			handleException(e);
		} finally {
			em.close();
		}
	}

	protected List<T> findAll(Class<T> clazz) {

		List<T> objects = null;
		try {
			startOperation();
			Query query = em.createQuery("from " + clazz.getName());
			objects = query.getResultList();
		} catch (HibernateException e) {
			handleException(e);
		} finally {
			em.close();
		}
		return objects;
	}

	protected void handleException(HibernateException e) throws DataAccessLayerException {

		em.getTransaction().rollback();
		throw new DataAccessLayerException(e);
	}

	protected void startOperation() throws HibernateException {

		em = DatabaseFactory.getInstance().createEntityManager();
		em.getTransaction().begin();
	}

	/**
	 * From stackoverflow: Most often this is DB-specific and if you can do it
	 * using JDBC you can do it also with JPA native query. You basically need
	 * to execute (the DB-specific) extra query to get the inserted ID.
	 * 
	 * @param clazz
	 * @return
	 * @throws SQLException
	 * @throws ClassNotFoundException
	 */
	protected Long lastGeneratedId() {

		try {
			// startOperation();
			Query query = em.createNativeQuery("SELECT MAX(ID) FROM #TABLE#".replaceAll("#TABLE#", getTableName()));
			BigInteger bigInteger = (BigInteger) query.getSingleResult();
			return new Long(bigInteger.longValue());
		} catch (HibernateException e) {
			handleException(e);
			return null;
		} finally {
			em.close();
		}

	}
}
